package cn.openea.perpartner.rule.compile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义类加载器
 * 根据类名在内存中查找是否已存在该类，若不存在则调用 URLClassLoader的 defineClass方法加载该类
 * URLClassLoader的具体作用就是将class文件加载到jvm虚拟机中去
 * @author Weird077
 */
public class DynamicClassLoader extends URLClassLoader {
    Logger logger = LoggerFactory.getLogger(getClass());
    Map<String, byte[]> classBytes = new HashMap<>();

    public DynamicClassLoader(ClassLoader parent) {
        super(new URL[0], parent);
    }
    public DynamicClassLoader(Map<String, byte[]> classBytes) {
        super(new URL[0], DynamicClassLoader.class.getClassLoader());
        this.classBytes.putAll(classBytes);
    }
    /**
     * 加载指定的java源码，得到Class对象
     * @param javaCode java源码
     * @return Class对象
     */
    public static Class<?> load(String javaCode) throws ClassNotFoundException {
        //先使用动态编译工具，编译java源码，得到类的全限定名和class字节码的字节数组信息
        Map<String, byte[]> bytecode = DynamicRuleCompiler.compileJavaCode(javaCode);
        if(bytecode != null) {
            //传入动态类加载器
            DynamicClassLoader classLoader = new DynamicClassLoader(bytecode);
            //加载得到Class对象
            return classLoader.loadClass(bytecode.keySet().iterator().next());
        } else {
            throw new ClassNotFoundException("class not found");
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] buf = classBytes.get(name);
        if (buf == null) {
            return super.findClass(name);
        }
        classBytes.remove(name);
        return defineClass(name, buf, 0, buf.length);
    }
    public  Map<String, byte[]> loadClassByName(String fullName) {
        byte[] classData = this.getBytes(fullName);
        classBytes.put(fullName,classData);
        return classBytes;
    }

    public byte[] getBytes(String classPath) {
        classPath = classPath.replace(".", "/") + ".class";
        classPath = DynamicRuleCompiler.getCompilePath() + classPath;
        File file = new File(classPath);
        long len = file.length();
        byte[] raw = new byte[(int)len];
        try {
            FileInputStream fin = new FileInputStream(file);
            int r = fin.read(raw);
            if ((long)r != len) {
                logger.error("{} read class file error!",classPath);
            }
            return raw;
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("{} can't read class file !",classPath);
            return null;
        }
    }
}